package gulimall.web;

import gulimall.entity.Category;
import gulimall.service.ICategoryService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author HLF
 * @date 2022/6/28 19:45
 */
@RestController
@Slf4j
@RequestMapping("/product")
public class RedissonDemo {
    @Resource
    private RedissonClient redissonClient;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource(name = "redisTemplate")
    private ValueOperations<String, Object> valueOps;
    @Resource
    private ICategoryService categoryService;

    /**
     * 可重入锁
     * 可重入锁的演示见gulimall笔记.md
     */
    @GetMapping("/redissonTest")
    public String redissonTest() {
        RLock lock = redissonClient.getLock("my-lock");

        try {
            // 尝试加锁，最多等待100秒(100s内获取锁失败返回false),具有watch dog机制
            // lockWatchdogTimeout/3 毫秒后(默认10s)进行自动续期
            // 最佳实战：使用指定过期时间的方式，不使用watch dog机制
            boolean tryLock = lock.tryLock(100, TimeUnit.SECONDS);//阻塞式等待
            if (tryLock) {
                System.out.println("加锁成功，执行业务逻辑..." + Thread.currentThread().getName());
                //执行业务逻辑
                Category byId = categoryService.getById(1);
                System.out.println(byId);

                Thread.sleep(2);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//手动解锁
            System.out.println("释放锁..." + Thread.currentThread().getName());
        }

        return redissonClient.toString();
    }

    /**
     * 读写锁作用：保证一定能读取到最新数据
     * <p>
     * 保证一定能读取到最新数据，修改期间，写锁是一个排他锁（互斥锁，独享锁）读锁是一个共享锁
     * 写锁没释放读锁就必须等待
     * 读 + 读 相当于无锁，并发读，只会在 redis中记录好，所有当前的读锁，他们都会同时加锁成功
     * 写 + 读 等待写锁释放
     * 写 + 写 阻塞方式
     * 读 + 写 有读锁，写也需要等待
     * 只要有写的存在，都必须等待
     * <p>
     * 读写锁-写
     */
    @GetMapping("/writeValue")
    public String writeValue() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");

        System.out.println("获取写锁...");
        RLock rLock = readWriteLock.writeLock();//获取写锁，写数据
        String message = UUID.randomUUID().toString();

        try {
            //尝试获取锁，超过40s返回false，获取到锁则返回true，具有看门狗机制
            boolean tryLock = rLock.tryLock(40, TimeUnit.SECONDS);
            if (tryLock) {
                valueOps.set("writeValue", message);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            System.out.println("释放写锁...");//如果不释放锁，看门狗机制会默认在业务结束后释放锁
            rLock.unlock();
        }

        return message;

    }

    /**
     * 读写锁-读
     */
    @GetMapping("/readValue")
    public String readValue() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");

        System.out.println("获取读锁...");
        RLock rLock = readWriteLock.readLock();//获取读锁，读取数据

        String readValue = "";
        try {
            boolean tryLock = rLock.tryLock(40, TimeUnit.SECONDS);
            if (tryLock) {
                readValue = (String) valueOps.get("writeValue");
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            System.out.println("释放读锁...");
            rLock.unlock();
        }
        return readValue;
    }

    /**
     * Semaphore
     * 信号量
     * 类似于JUC的Semaphore
     */
    //停车
    @GetMapping("/parkIn")
    public String parkIn() {
        RSemaphore park = redissonClient.getSemaphore("park");

        boolean b = park.tryAcquire();//获取一个信号，获取一个值，占用一个车位

        return "parkInIsOK：" + b;
    }

    //取车
    @GetMapping("/parkOut")
    public String parkOut() {
        RSemaphore park = redissonClient.getSemaphore("park");

        park.release();

        return "parkOut Ok";
    }

    /**
     * 闭锁
     * 类似于JUC的CountDownLatch
     * 例子：有五个班，必须所有班都走完才能锁门
     */
    //等待闭锁完成后(所有班级的人都走完了)，方法才会执行完
    @GetMapping("/lockDoor")
    public String lockDock() throws InterruptedException {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        door.trySetCount(5);
        door.await();//等待闭锁完成


        return "闭所完成，所有班级都走完了，关门";
    }


    @GetMapping("/leave/{classId}")
    public String leave(@PathVariable("classId") String classId) {
        RCountDownLatch door = redissonClient.getCountDownLatch("door");
        door.countDown();//计数器减一

        return classId + "班的人走完了！";
    }
}
